小程序核心知识点
相关资料地址:
猫眼电影
有赞小程序ui库
(一) 静态页面
(1) 使用小程序组件
- view 相当于 div
- text 相当于span
- img变为image
- 地图,音视频,画布等
- page{} 是每个页面的根元素
(2) 移动端适配
使用 rpx 作为单位,这个屏幕的宽度为 750rpx
(3) 底部 tab 配置
文档=>框架=>全局配置=>tabBar
(4) 开放数据
文档=>组件=>开放能力=>open-data
(5) 编译模式, 编译模式添加启动参数
// 列表跳详情的时候, 会带上id, 比如 url="/detail/detail?id=xxxx"
// 如果此时切换到了detail页面的模式, 刷新数据就没有了
// 这个时候可以编辑detail页面的编译模式, 添加启动参数即可解决问题
(6) 在小程序中使用 vant-ui
文档地址: https://vant-contrib.gitee.io/vant-weapp/#/home // 文档地址
具体步骤:
安装vant-ui插件
# 通过 npm 安装 npm i @vant/weapp -S --production
构建npm
让安装的包能在小程序中使用
修改 app.json
将 app.json 中的
"style": "v2"
去除,小程序的新版基础组件强行加上了许多样式,难以覆盖,不关闭将造成部分组件样式混乱。修改 project.config.json
{ ... "setting": { ... "packNpmManually": true, "packNpmRelationList": [ { "packageJsonPath": "./package.json", "miniprogramNpmDistDir": "./miniprogram_npm/" } ] } }
导入组件
在app.json中导入
"usingComponents": { "van-button": "@vant/weapp/button/index" }
使用组件
<van-button type="default">默认按钮</van-button> <van-button type="primary">主要按钮</van-button> <van-button type="info">信息按钮</van-button> <van-button type="warning">警告按钮</van-button> <van-button type="danger">危险按钮</van-button>
(7) 小程序中使用 less
(二) 生命周期
小程序有以下生命周期
- onLoad 生命周期函数--监听页面加载`
- onReady 生命周期函数--监听页面初次渲染完成
- onShow 生命周期函数--监听页面显示
- onHide 生命周期函数--监听页面隐藏
- onUnload 生命周期函数--监听页面卸载
- onPullDownRefresh 页面相关事件处理函数--监听用户下拉动作
- onReachBottom 页面上拉触底事件的处理函数
- onShareAppMessage 用户点击右上角分享
注意:
- onLoad 只会在小程序中执行一次,比如当前页面是 a 页面,我们在 onLoad 发送请求去获取数据,然后我们切到 b 页面,再切回 a 页面的时候,onLoad 不会再触发,也就是不会再重新发请求获取数据
- onShow 跟 onLoad 不同,每次切回 a 页面的时候都会触发这个生命周期函数
- 所以到底是在 onLoad 发请求还是在 onShow 发请求,要根据实际的业务去做
(三) 获取数据
前言
- 小程序只支持 https
- 需要到小程序后台配置域名白名单
- 项目中请求了非 https 和不在域名白名单上的接口会报错
- 开发时可以取消域名校验,就可以请求任意接口,设置方法 小程序右上角详情 =》本地设置 => 不校验合法域名...
(一) 发送请求
文档地址 文档首页 => api => 网络 => 发起请求
// 示例
wx.request({
url: "test.php", //仅为示例,并非真实的接口地址
// 请求参数
data: {
x: "",
y: "",
},
method: "get", // 请求类型
header: {
"content-type": "application/json", // 默认值
},
// 成功的回调
success(res) {
console.log(res.data);
},
// 失败的回调
fail(error) {
console.log(error);
},
// 不管是成功还是失败都会调用此方法
complete() {
console.log("done");
},
});
(二) 使用 promise 封装请求
- 配置 baseUrl
// config.js
let env = "prod";
let baseUrl = "";
if (env === "dev") {
// 本地地址
baseUrl = "https://localhost:3009";
} else if (env === "prod") {
baseUrl = "https://huruqing.cn:3009";
}
// 导出
export { baseUrl };
- 新建/utils/reques.js 文件,内容如下
import { baseUrl } from "./config.js";
/**
* 封装请求
* url:请求地址
* data:请求参数
* method: 请求类型
*/
const request = (url, data, method) => {
// 获取token,登录时存的
let token = wx.getStorageSync("token");
url = baseUrl + url;
return new Promise((resolve, reject) => {
// 请求
wx.request({
url,
method,
data,
header: {
"user-token": token,
},
success: (res) => {
if (res.data.code == 666) {
resolve(res.data);
} else if (res.data.code == 603) {
wx.removeStorageSync("token");
wx.showModal({
title: "提示",
content: "登录已过期,是否重新登录",
success(res) {
if (res.confirm) {
// 跳转到个人中心页面
wx.switchTab({
url: "/pages/my/my",
});
} else if (res.cancel) {
console.log("用户点击取消");
}
},
});
} else {
reject(res.data.msg);
}
},
fail: (err) => {
reject("网络异常");
},
});
});
};
const get = (url, data) => {
return request(url, data, "get");
};
const post = (url, data) => {
return request(url, data, "post");
};
export default {
get,
post,
};
- 挂载到 app,在页面中就不需要重复加载
// app.js
import request from "./utils/request.js";
App({
onLaunch: function() {
this.$get = request.get;
this.$post = request.post;
},
});
- 在页面中使用
// my.js
// 获取app对象
const app = getApp();
Page({
onShow() {
this.getData();
},
// 发送请求
getData() {
let url = "xxxxx";
let data = { xxx: xxx };
app
.$get(url, data)
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
},
});
(四) 渲染页面
- data 和 setData
Page({
data: {
count: 1,
},
changeCount() {
// 获取data里count的值
let count = this.data.count;
// 加1
this.setData({
count: ++count,
});
},
});
- 插值表达式
, wxml 中所有的变量都使用
(除了wx:key)
- 如果数组成员是字符串或者数字,
wx:key="index"或者wx:key="*this"
- 如果数组成员是对象, 比如
[{name:'zs',id:1}],wx:key="id"
- 如果数组成员是字符串或者数字,
- 条件渲染
wx:if
- 列表渲染
wx:for
默认有 item 和 index - 双重
wx:for
时需要其中一个指定 item 和 index
(五) 绑定事件
文档地址 : 文档首页 => 指南 => 事件系统
// 例子1 小程序
<view data-username="hurqing" bindtap="tapName"> Click me! </view>
Page({
tapName: function(event) {
// 小程序事件不能像vue那样传参,只能通过自定义属性来传参
let username = event.target.dataset.username;
console.log(username);
},
});
注意: wxml和html一样, 不区分大小写, 页面上的大写, 最后变成了小写
- 常见事件类型 | 类型 | 触发条件 | 最低版本 | | --- | --- | --- | | touchstart | 手指触摸动作开始 | | | touchmove | 手指触摸后移动 | | | touchcancel | 手指触摸动作被打断,如来电提醒,弹窗 | | | touchend | 手指触摸动作结束 | | | tap | 手指触摸后马上离开 | | | longpress | 手指触摸后,超过 350ms 再离开,如果指定了事件回调函数并触发了这个事件,tap 事件将不被触发 | 1.5.0 | | longtap | 手指触摸后,超过 350ms 再离开(推荐使用 longpress 事件代替) | | | transitionend | 会在 WXSS transition 或 wx.createAnimation 动画结束后触发 | | | animationstart | 会在一个 WXSS animation 动画开始时触发 | | | animationiteration | 会在一个 WXSS animation 一次迭代结束时触发 | | | animationend | 会在一个 WXSS animation 动画完成时触发 | | | touchforcechange | 在支持 3D Touch 的 iPhone 设备,重按时会触发 | |
(六) 页面跳转
小程序常用两种方式,普通页面跳转和 tab 栏切换(跳转)
- 跳转方式一 通过组件 navigator 进行跳转,需要指定跳转类型 open-type
<view class="btn-area">
// 普通页面跳转
<navigator
url="/page/navigate/navigate?title=navigate"
hover-class="navigator-hover"
>跳转到新页面</navigator>
// 跳转到tab栏
<navigator
url="/page/index/index"
open-type="switchTab"
hover-class="other-navigator-hover">切换 Tab</navigator>
// 重定向
<navigator
url="../../redirect/redirect/redirect?title=redirect"
open-type="redirect"
hover-class="other-navigator-hover"
>在当前页打开</navigator
>
// 跳转到小程序
<navigator
target="miniProgram"
open-type="navigate"
app-id=""
path=""
extra-data=""
version="release"
>打开绑定的小程序</navigator
>
</view>
- 跳转方式二 通过 js 进行跳转
// 普通页面跳转
wx.navigateTo({
url: "/product/product",
});
// tab栏切换
wx.switchTab({
url: "/index",
});
(七) 路由传参
// 列表页
<navigator wx:for="{{flowerList}}" class="flex item pl-10 bg-fff" url="/pages/detail/detail?id={{item.id}}"></navigator>
// 详情页
onLoad: function (options) {
console.log(options.flowerId);
},
// 参数为对象时,要先将对象转成字符串
toDetail() {
let obj = {useranme: 'zs',age:100};
// 将对象转成json字符串
let query = JSON.stringify(obj);
// 传参
wx.navigateTo({
url: '/pages/detail/detail?query='+query,
})
},
// 获取参数
onLoad: function (options) {
let obj = JSON.parse(options.query);
console.log(obj);
},
注意: wx.swichTab跳转url后不能带参数
(八) 数据缓存
- 异步方法
wx.setStorage({
key: "key",
data: "value",
});
wx.getStorage({
key: "key",
success(res) {
console.log(res.data);
},
});
- 同步方法
wx.setStorageSync("key", "value");
// 例子
let token = 'asdfasdfasjdflasdjf;asdf;asdfjsak;ldf';
wx.setStorageSync('token',token);
let token = wx.getStorageSync('token');
(九) 用户授权
知识点
- 用户授权: 录音授权
- 检查用户是否已经授权
- 获取用户信息的授权操作
用户授权功能列表 除了开放数据以外,小程序的很多功能都需要获得用户授权,才可以进行下一步的操作, 用户授权的功能列表
(1) 用户授权之录音授权
onLoad: function (options) {
wx.authorize({
// 授权类型
scope: "scope.record", // scope.camera 摄像头
success() {
// 用户已经同意小程序使用录音功能,才能调用录音相关接口
console.log('授权成功');
},
fail() {
console.log('想要使用录音功能, 请先授权');
}
});
}
// 开始录音
Page({
onLoad: function (options) {
let obj = wx.getRecorderManager();
obj.start({
format: 'mp3'
});
obj.onStart((aa) => {
console.log('开始录音');
})
setTimeout(() => {
obj.stop();
obj.onStop(res => {
console.log(res);
console.log('停止录音')
})
}, 3000)
},
})
(2) 获取用户信息的授权
- scope.userInfo获取用户信息的授权无法通过js直接调起授权窗口
- 必须绑定点击事件, 用户去点击才能调起授权, 我想应该是腾讯想提醒用户要慎重
// demo.wxml
<button bindtap="getUserProfile"> 获取用户信息 </button>
// demo.js
getUserProfile(e) {
wx.getUserProfile({
desc: '用于完善会员资料',
success: (res) => {
console.log(res);
}
})
},
(3) 获取用户的授权信息
获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限
wx.getSetting({
success (res) {
console.log(res.authSetting)
}
})
(十) 小程序登录
前言
在做小程序电商项目的时候,添加一个商品到购物车,后台需要知道是哪个用户执行了添加的操作,这里就涉及到了用户识别的操作.
整个流程如下:
- 前端调用后台的登录接口,获得 token
- 前端发送请求带上 token,后台可以通过 token 来识别用户
在第一步中,有分为以下几个步骤
- 前端首先调用 wx.login 获取 code,
- 紧接着需要调用 wx.getUserInfo 来获得用户信息 (需要用户授权) ,获得 iv 和 encryptedData
- 调用后台登录接口,比如 /user/login,把以上三个参数
{
code, iv, encryptedData;
}
- 传给后台
- 后台根据上面三个参数调用微信接口获得 openid,再将 openid 和用户的其他信息一并存起来,同时生成 userId,根据 userId 生成一个加密的 token 返回给小程序,小程序端再把 token 放入请求头,每次发请求都带上,这样后台就可以识别用户了
import { baseUrl } from "./config.js";
const request = (url, data, method) => {
// 获取token,登录时存的
let token = wx.getStorageSync("token");
url = baseUrl + url;
return new Promise((resolve, reject) => {
// 请求
wx.request({
url,
method,
data,
// 把token加入到请求头
header: {
"user-token": token,
},
success: (res) => {
if (res.data.code == 666) {
resolve(res.data);
} else if (res.data.code == 603) {
wx.removeStorageSync("token");
wx.showModal({
title: "提示",
content: "登录已过期,是否重新登录",
success(res) {
if (res.confirm) {
// 跳转到个人中心页面
wx.switchTab({
url: "/pages/my/my",
});
} else if (res.cancel) {
console.info("用户点击取消");
}
},
});
} else {
// wx.dialog(res.data.msg);
wx.showToast({
title: res.data.msg,
icon: 'none',
duration: 1500
})
}
},
fail: (err) => {
wx.showToast({
title: '网络异常',
icon: 'none',
duration: 1500
})
},
});
});
};
const get = (url, data) => {
return request(url, data, "get");
};
const post = (url, data) => {
return request(url, data, "post");
};
export default {
get,
post,
};
(十一) 小程序获取位置
(1) 获取位置信息
wx.getLocation({
type: 'wgs84',
success: (res) => {
const latitude = res.latitude;
const longitude = res.longitude;
},
fail: (err) => {
this.setData({
city: '定位失败,请手动选择'
})
}
})
(2) 实际应用:
获取城市名称(需要上一点提供的经纬度)
wx.request({ url: `https://apis.map.qq.com/ws/geocoder/v1/?location=${latitude},${longitude}&key=XBZBZ-OBG63-LOD3N-3QR5Q-X6Z2Q-BFBIR`, success: (res) => { let cityName = res.data.result.address_component.city; console.log(cityName) } })
计算两个坐标之间的距离
(十二) 支付流程
- 前端请求后端创建订单接口
- 后端调用小程序统一下单 API, 成功之后给前端返回支付所需参数
- 小程序端使用 wx.requestPayment调起支付窗口
<button bindtap="payNow">立即支付</button>
payNow() {
wx.requestPayment({
timeStamp: '时间戳,后台返回',
nonceStr: '随机字符串,后台返回',
package: '统一下单接口返回的 prepay_id 参数值,后台返回',
signType: '签名类型,后台返回',
paySign: '签名,后台返回',
// 支付成功的回调
success(res) {
console.log('支付成功');
},
// 支付失败的回调
fail(err) {
console.log('支付失败');
},
});
}
(十三) 自定义组件
自定义一个导航组件
新建nav文件夹, 右键nav, 新建组件
编写组件静态
在父组件(demo.json) 注册组件, 添加代码
{ "usingComponents": { "Nav":"./nav/nav" } }
在wxml中使用组件
<Nav></Nav>
父子组件通信
- 父传子, 使用属性, 子组件通过
properties:{}
接收 - 子传父
- 父组件绑定自定义事件
bind:xxx
- 子组件触发自定事件xxx
this.triggerEvent('xxx',{数据对象})
- 父组件绑定自定义事件
- 父传子, 使用属性, 子组件通过
完整代码:
父组件
// demo.wxml
<Nav title="购物车" bind:clickLeft="clickLeft"></Nav>
// demo.json
{
"usingComponents": {
"Nav":"./nav/nav"
}
}
// demo.js
Page({
clickLeft() {
console.log('左按钮被点击了')
}
})
子组件
// nav.wxml
<view class="header">
<text bindtap="clickLeft">左按钮</text>
<view class="title">{{title}}</view>
<text class="button">右按钮</text>
</view>
// nav.js
Component({
properties: {
title: String
},
methods: {
clickLeft() {
this.triggerEvent('clickLeft');
},
})
// nav.wxss
page {
background: #ebebeb;
}
.header {
display: flex;
height: 50px;
background-color: green;
justify-content: space-between;
align-items: center;
padding: 0 15px;
color: #fff;
}